#include "kpextSyncMisc.h"
#include "kpextVkContext.h"
#include "kpextOpImgCvtF32C1toU16C1.h"
#include "kpextOpImgCvtU16C1toF32C1.h"
#include "kpextOpImgCpyF32C1.h"
#include "kpextOpImgRWinF32C1.h"
#include "kpextOpImgCvtF32C1toF32C2.h"
#include "kpextOpImgF32C2FFT.h"
#include "kpextOpImgF32C2Magnitude.h"
#include "kpextOpImgF32C1Average.h"
#include "kpextOpImgF32C1Transpose.h"
#include "kpextOpImgF32C1ROI.h"
#include "kpextOpImgF32C1Flip.h"
#include "kpextOpImgF32C1MulPow.h"
#include "kpextOpImgCMapF32C1toU8C4.h"
#include "kpextOpImgResampleU16C1toF32C1.h"

#include "KpExtGP.h"

using namespace kpext;

typedef struct _Stage1OpSZ{
    std::shared_ptr<CommandBuffer> cmdbuf = nullptr;
    //    std::shared_ptr<CmdBaseBufferCpy> bufcpy = nullptr;
    //    std::shared_ptr<CmdBaseBufferSet> bufset = nullptr;

    std::shared_ptr<CmdBuffersMemBarrier> inbarrier = nullptr;
    std::shared_ptr<CmdBuffersMemBarrier> outbarrier = nullptr;

    std::shared_ptr<StagingBuffer> inputMem = nullptr;
    std::shared_ptr<StagingBuffer> outputMem = nullptr;

    std::shared_ptr<DeviceBuffer> devIdxBuf = nullptr;
    std::shared_ptr<DeviceBuffer> devBuf1 = nullptr;
    std::shared_ptr<DeviceBuffer> devBuf2 = nullptr;
    std::shared_ptr<DeviceBuffer> devBuf3 = nullptr;
    std::shared_ptr<DeviceBuffer> devAvgBuf = nullptr;

    std::shared_ptr<OpImgCvtU16C1toF32C1> ccvtU16C1toF32C1 = nullptr;  //  16位无符号图转为单通道浮点图
    std::shared_ptr<OpImgResampleU16C1toF32C1> cresampleU16C1toF32C1 = nullptr;  //  16位无符号图重采样为单通道浮点图

    std::shared_ptr<OpImgCpyF32C1> ccpyF32C1 = nullptr;     //  单通道浮点图复制操作
    std::shared_ptr<OpImgRWinF32C1> crwinF32C1 = nullptr;   //  单通道浮点图FFT前每行开窗操作
    std::shared_ptr<OpImgCvtF32C1toF32C2> ccvtF32C1toF32C2 = nullptr;  //  单通道浮点图转为双通道浮点图
    std::shared_ptr<OpImgF32C2FFT> cF32C2fft = nullptr;     //  双通道浮点图行或列FFT操作
    std::shared_ptr<OpImgF32C2Magnitude> cF32C2amp = nullptr; //  双通道浮点图求复数模(幅值)
    std::shared_ptr<OpImgF32C1Average> cF32C1Avg = nullptr;     //  单通道浮点图多帧平均操作
    std::shared_ptr<OpImgF32C1Transpose> cF32C1Trans = nullptr;  //  单通道浮点图转置操作
    std::shared_ptr<OpImgF32C1ROI> cF32C1ROI = nullptr;     //  单通道浮点图感兴趣区域提取操作
    std::shared_ptr<OpImgF32C1Flip> cF32C1Flip = nullptr;   //  单通道浮点图水平翻转操作

    std::shared_ptr<OpImgCvtF32C1toU16C1> ccvtF32C1toU16C1 = nullptr;  //  单通道浮点图转为16位无符号图

}Stage1OpSZ;  // 图像处理阶段1 的操作杂项

typedef struct _CfgStage1{
    // 输入时的列数、行数
    int cols_input;
    int rows_input;
    int type_input;   // 0为16位无符号原始数据， 1为32位浮点数据(求完幅值后的数)

    // 进行FFT时的列数、行数
    int cols_fft;
    int rows_fft;

    float resample_delta;  //  用于色散补偿的重采样索引变形量

    int win_type;   // 进行FFT前开窗类型

    //  FFT后求幅值后的列数、行数
    int cols_amp;
    int rows_amp;

    int avg_count;  //   FFT后求幅值后多少帧图像进行平均

    bool disable_log;  // 求幅值后是否禁用log运算
    float prescale;   // 求幅值log前乘以的倍率
    float alpha;      // 求幅值log后乘以的倍率
    float beta;       //  求幅值log乘以倍率后相加的偏移值

    //  转置后的列数、行数
    int cols_trans;
    int rows_trans;

    //  ROI操作的上、左、右、下剪裁
    int roi_top;
    int roi_left;
    int roi_right;
    int roi_bottom;

    bool enable_hflip;  // ROI后是否进行水平翻转
    bool enable_vflip;  // ROI后是否进行垂直翻转

    // 输出时的列数、行数
    int cols_output;
    int rows_output;
}CfgStage1;  //  图像处理阶段1 配置结构


typedef struct _Stage2OpSZ{
    struct Size2D{
        int cols;
        int rows;
    };

    std::shared_ptr<struct Size2D> framesize = nullptr;
    std::shared_ptr<CommandBuffer> cmdbuf = nullptr;

    std::shared_ptr<CmdBuffersMemBarrier> inbarrier = nullptr;
    std::shared_ptr<CmdBuffersMemBarrier> outbarrier = nullptr;

    std::shared_ptr<StagingBuffer> inputMem = nullptr;
    std::shared_ptr<StagingBuffer> outputMem = nullptr;

    std::shared_ptr<DeviceBuffer> devBuf1 = nullptr;
    std::shared_ptr<DeviceBuffer> devBuf2 = nullptr;
    std::shared_ptr<DeviceBuffer> devBuf3 = nullptr;

    std::shared_ptr<OpImgCvtU16C1toF32C1> ccvtU16C1toF32C1 = nullptr;  //  16位无符号图转为单通道浮点图
    std::shared_ptr<OpImgF32C1Flip> cF32C1Flip = nullptr;   //  单通道浮点图水平翻转操作
    std::shared_ptr<OpImgF32C1MulPow> cF32C1MulPow = nullptr;  //  乘a 阈值  y次幂单通道浮点图处理
    std::shared_ptr<OpImgCMapF32C1U8C4> ccmapF32C1toU8C4 = nullptr;  //  为单通道浮点图0~65535值转换为8位四通道无符号图处理

}Stage2OpSZ;  // 图像处理阶段1 的操作杂项

typedef struct _CfgStage2{
    bool enable_hflip;  // 是否进行水平翻转
    bool enable_vflip;  // 是否进行垂直翻转

    uint16_t threshold;  //  过滤操作的阈值
    float offset_b;     //  乘以a，前加上的偏移值
    float mul_a;        //  乘以a，调节亮度
    float power_y;      //  y次幂，调节对比度

    uint32_t colormap;      //  颜色映射号
    uint32_t colormapupdate;    //  颜色映射数据更新
}CfgStage2;  //  图像处理阶段2 配置结构


typedef std::packaged_task<void (Stage1OpSZ &)> Stage1Task;  //  图像处理阶段1  的任务函数定义
typedef std::packaged_task<void (Stage2OpSZ &)> Stage2Task;  //  图像处理阶段2  的任务函数定义

static uint32_t stage1ThreadsCnt = 0;    //  阶段1的线程数量
static std::vector<Stage1OpSZ> stage1Op;  //  阶段1的操作杂项数据

static uint32_t stage2ThreadsCnt = 0;    //  阶段2的线程数量
static std::vector<Stage2OpSZ> stage2Op;  //  阶段2的操作杂项数据

static std::vector<std::shared_ptr<Stage1Task>> stage1Tasks;  //  阶段1的任务包装
static std::vector<std::shared_ptr<std::thread>> stage1Threads;  // 阶段1的线程

static std::vector<std::shared_ptr<Stage2Task>> stage2Tasks;  //  阶段2的任务包装
static std::vector<std::shared_ptr<std::thread>> stage2Threads;  // 阶段2的线程

static CfgStage1 cfgstage1{};  //  真正用于阶段1线程的配置结构体
static CfgStage1 cfgstage1_temp{};  //  给外部调用修改的阶段1线程的临时配置结构体

static int stage1RingBufferFrames = 0;      //  阶段1环形帧缓冲数量
static int stage1RingBufferFrameIdx = 0;    //  阶段1环形帧缓冲当前索引
static std::vector<std::shared_ptr<uint16_t>> stage1FrameBuf;  //  阶段1环形帧缓冲

static CfgStage2 cfgstage2{};  //  用于阶段2线程的配置结构体

typedef struct{
    void * inptr;
    uint16_t * outptr;
}Stage1InputSZ;

typedef SemWrapNtoOneT<Stage1InputSZ, uint16_t *> Stage1Semaphore;
static std::shared_ptr<Stage1Semaphore> stage1Sem(nullptr);  //  阶段1的信号量
static std::mutex stage1CtrlMutex;   //  阶段2的启 停 压 弹互斥锁

typedef struct{
    int cols;
    int rows;
    uint16_t * inptr;
    uint8_t * outptr;
}Stage2InputSZ;

typedef struct{
    int cols;
    int rows;
    uint8_t * dptr;
}Stage2OutputSZ;

typedef SemWrapNtoOneT<Stage2InputSZ, Stage2OutputSZ> Stage2Semaphore;
static std::shared_ptr<Stage2Semaphore> stage2Sem(nullptr);  //  阶段2的信号量
static std::mutex stage2CtrlMutex;   //  阶段2的启 停 压 弹互斥锁

static std::atomic<bool> stage1Working(false);  //  阶段1的线程运行中标记
static std::atomic<bool> stage2Working(false);  //  阶段2的线程运行中标记


/**
 * @brief 计算生成重色散补偿采样索引，公式为$n^{'} = a n(a+1 - \frac{n}{N-1})^{-1}$，其中$a = 0.5 * (\frac{0.5}{delta} - 1)$
 * @param N  索引个数
 * @param delta  中间索引值差除以N的大小
 * @return  返回生成的缓冲区，缓冲区由调用方进行释放
 */
static float * genDCResampleIdx(int N, float delta){
    if(N <= 0){
        return nullptr;
    }
    float * ret = new float[static_cast<size_t>(N)];
    std::memset(ret, 0, static_cast<size_t>(N) * sizeof (float));
    for (int n = 0; n < N; ++n) {
        float a = 0.5f * (0.5f / delta  - 1);
        float t =  static_cast<float>(n) / static_cast<float>(N - 1);
        t = a + 1 - t;
        t = 1 / t;
        t = a * static_cast<float>(n) * t;
        ret[n] = t;
    }
    return ret;
}


void stage1Work(Stage1OpSZ & op);
void stage2Work(Stage2OpSZ & op);

void kpextSetVKValidationLayerEn(bool enable)
{
    kpextVkContext::EnableDebugLayers = enable;
    kpextVkContext::EnableDebugLayersByProgram = true;
    const auto fs = "Set vulkan validation layer {} by program !";
    if(IS_CONSOLE_SUPORT_COLOR){
        SPDLOG_WARN(fs, fmt::format(fmt::fg(fmt::color::yellow), "enable={}", enable));
    }else{
        SPDLOG_WARN(fs, fmt::format("enable={}", enable));
    }
}


void kpextSetStagesThreadsCount(unsigned int stage1TCnt, unsigned int stage2TCnt)
{
    if(stage1TCnt > 0){
        stage1ThreadsCnt = stage1TCnt;
        SPDLOG_WARN("Image process stage1 set to use {} threads.", stage1ThreadsCnt);
    }

    if(stage2TCnt > 0){
        stage2ThreadsCnt = stage2TCnt;
        SPDLOG_WARN("Image process stage2 set to use {} threads.", stage2ThreadsCnt);
    }
}

unsigned int kpextGetStage1ThreadsCount(){
    return stage1ThreadsCnt;
}

unsigned int kpextGetStage2ThreadsCount(){
    return stage2ThreadsCnt;
}

int kpextInit()
{
    int ret = -1;
    if(GvkContextPtr){
        return 0;
    }

    GvkContextPtr = new kpextVkContext();
    if(GvkContextPtr && GvkContextPtr->getIsVkContextOk()){
        ret = 0;
    }else{
        return ret;
    }

    uint32_t queuesCnt = GvkContextPtr->getComputeQueuesCount();
    uint32_t queIdx = 0;
    if(queuesCnt < 1){
        queuesCnt = 1;
    }

    //  阶段1 线程资源
    if(stage1ThreadsCnt < 1
            && kpextVkContext::StagesThreadsCount.size() > 0){
        stage1ThreadsCnt = kpextVkContext::StagesThreadsCount[0];
    }

    if(stage1ThreadsCnt < 1) stage1ThreadsCnt = 1;

    stage1Op.resize(stage1ThreadsCnt);
    for (uint32_t i = 0; i < stage1ThreadsCnt; ++i) {
        stage1Op[i].cmdbuf = GvkContextPtr->cmdbuf(queIdx);
        queIdx++;
        queIdx = queIdx % queuesCnt;
    }

    //  阶段2 线程资源
    if(stage2ThreadsCnt < 1
            && kpextVkContext::StagesThreadsCount.size() > 0){
        stage2ThreadsCnt = kpextVkContext::StagesThreadsCount[1];
    }

    if(stage2ThreadsCnt < 1) stage2ThreadsCnt = 1;

    stage2Op.resize(stage2ThreadsCnt);
    for (uint32_t i = 0; i < stage2ThreadsCnt; ++i) {
        stage2Op[i].cmdbuf = GvkContextPtr->cmdbuf(queIdx);
        queIdx++;
        queIdx = queIdx % queuesCnt;
    }

    return ret;
}


int kpextRelease()
{
    if(GvkContextPtr){
        if(stage1Working.load()){
            kpextStopStage1();
        }

        for (uint32_t i = 0; i < stage1ThreadsCnt; ++i) {
            stage1Op[i] = Stage1OpSZ{nullptr};
        }

        if(stage2Working.load()){
            kpextStopStage2();
        }

        for (uint32_t i = 0; i < stage2ThreadsCnt; ++i) {
            stage2Op[i] = Stage2OpSZ{nullptr};
        }

        delete GvkContextPtr;
        GvkContextPtr = nullptr;
    }
    kpextVkContext::destroyInstance();
    return 0;
}

void kpextSetVKPhyDeviceIndex(int index)
{
    if(index < 0) index = -1;
    kpextVkContext::VK_PHY_DEV_IDX_TO_CREATE = index;
    SPDLOG_INFO("Set to use vulkan physicalDevices[{}] by program.",
                kpextVkContext::VK_PHY_DEV_IDX_TO_CREATE
                );
    if(kpextVkContext::VK_PHY_DEV_IDX_TO_CREATE >= 0){
        kpextVkContext::NO_USE_JSON_CONFIG = true;
    }else{
        kpextVkContext::NO_USE_JSON_CONFIG = false;
    }
}

void kpextSetVKPhyDeviceReqPUUID(const char *puuid)
{
    kpextVkContext::VK_PHY_DEV_IDX_TO_CREATE = -1;
    if(puuid){
        kpextVkContext::VK_PHY_DEV_PUUID_TO_CREATE = std::string(puuid);
        kpextVkContext::NO_USE_JSON_CONFIG = true;
    }else{
        kpextVkContext::VK_PHY_DEV_PUUID_TO_CREATE.clear();
        kpextVkContext::NO_USE_JSON_CONFIG = false;
    }

    SPDLOG_INFO("Set to use vulkan physicalDevice by program "
                "which pipelinecacheuuid is '{}'.",
                kpextVkContext::VK_PHY_DEV_PUUID_TO_CREATE
                );
}

void kpextSetVKQueueFamilyIndices(unsigned int *indices, int size)
{
    if(indices && size < 1) return;

    kpextVkContext::VK_QUEUE_FAMILY_INDICES.clear();
    if(indices){
        kpextVkContext::VK_QUEUE_FAMILY_INDICES.insert(
                    kpextVkContext::VK_QUEUE_FAMILY_INDICES.begin(),
                    indices,
                    indices + size);
    }

    SPDLOG_INFO("Set to create vulkan queueFamily[{}] by program.",
                fmt::format("{}",
                            fmt::join(kpextVkContext::VK_QUEUE_FAMILY_INDICES, ",")
                            )
                );
}

void kpextSetVKDeviceReqExtensions(const char * extsptr)
{
    std::vector<std::string> extensions;
    if(extsptr){
        auto startptr = extsptr;
        for (int i = 0; i < 64 * 1000; ++i) {
            if(*startptr == 0) break;

            auto endptr = extsptr + i;
            if(*endptr == ',' || *endptr == 0){
                if(endptr >  startptr){
                    auto s = std::string(startptr,endptr);
                    //                    fmt::print(s);
                    extensions.push_back(s);
                    if(*endptr == 0){
                        break;
                    }
                    startptr = endptr + 1;
                }

            }
        }
    }

    kpextVkContext::VK_DESIRED_DEV_EXTENSIONS.clear();

    if(extsptr){
        for(const auto & item : extensions){
            kpextVkContext::VK_DESIRED_DEV_EXTENSIONS.push_back(
                        item
                        );
        }
    }

    SPDLOG_INFO("Set to use vulkan device extensions[{}] by program.",
                fmt::format("{}",
                            fmt::join(kpextVkContext::VK_DESIRED_DEV_EXTENSIONS, ",")
                            )
                );
}

void kpextSetVKComputeShaderDefWGS(unsigned int workgroupsize_x,
                                   unsigned int workgroupsize_y,
                                   unsigned int workgroupsize_z)
{
    auto & workgroupsize = kpextVkContext::VK_DEF_COMPUTESHADER_WORKGROUP_SIZE;
    workgroupsize.clear();
    workgroupsize.resize(3);
    workgroupsize[0] = workgroupsize_x;
    workgroupsize[1] = workgroupsize_y;
    workgroupsize[2] = workgroupsize_z;

    for (auto & s : workgroupsize) {
        if(s == 0) s = 1;
    }

    const auto fstr = "compute shader default"
                      " {} by program !";
    std::string tstr;
    if(IS_CONSOLE_SUPORT_COLOR){
        tstr = fmt::format(fmt::fg(fmt::color::yellow),
                           "workgroupsize set to [{}]",
                           fmt::join(workgroupsize, ", ")
                           );
    } else {
        tstr = fmt::format("workgroupsize set to [{}]",
                           fmt::join(workgroupsize, ", ")
                           );
    }
    tstr = fmt::format(fstr, tstr);
    SPDLOG_WARN(tstr);
}

void kpextCfgStage1Input(int cols, int rows, int type)
{
    if(cols < 0) cols = 1;
    if(rows < 0) rows = 1;
    cfgstage1_temp.cols_input = cols;
    cfgstage1_temp.rows_input = rows;
    if(type != 0){
        cfgstage1_temp.type_input = 1;
    }else{
        cfgstage1_temp.type_input = 0;
    }
}

void kpextCfgStage1DCResample(float delta){
    cfgstage1_temp.resample_delta = delta;
}

void kpextCfgStage1Win(int win_type)
{
    if(win_type == 0){
        cfgstage1_temp.win_type = 0;
    }else{
        cfgstage1_temp.win_type = 1;
    }
}

void kpextCfgStage1Amp(float alpha, float beta, bool disable_log)
{
    cfgstage1_temp.alpha = alpha;
    cfgstage1_temp.beta = beta;
    cfgstage1_temp.disable_log = disable_log;
}

void kpextCfgStage1Avg(int avg_cnt)
{
    if(avg_cnt < 1) avg_cnt = 1;
    cfgstage1_temp.avg_count = avg_cnt;
}

void kpextCfgStage1ROI(int top, int left, int right, int bottom)
{
    if(top < 0) top = 0;
    if(left < 0) left = 0;
    if(right < 0) right = 0;
    if(bottom < 0) bottom = 0;
    cfgstage1_temp.roi_top = top;
    cfgstage1_temp.roi_left = left;
    cfgstage1_temp.roi_right = right;
    cfgstage1_temp.roi_bottom = bottom;
}

void kpextCfgStage1Flip(bool hflip, bool vflip)
{
    cfgstage1_temp.enable_hflip = hflip;
    cfgstage1_temp.enable_vflip = vflip;
}

bool kpextApplyCfgStage1()
{
    if(stage1Working.load()){
        return false;
    }

    int itemp = 1;
    for (int i = 1; i < 32; ++i) {
        itemp = itemp << 1;
        if(itemp >= cfgstage1_temp.cols_input){
            cfgstage1_temp.cols_fft = itemp;
            break;
        }
    }

    cfgstage1_temp.prescale = static_cast<float>(cfgstage1_temp.cols_fft);

    cfgstage1_temp.rows_fft = cfgstage1_temp.rows_input;

    cfgstage1_temp.cols_amp = cfgstage1_temp.cols_fft / 2;
    cfgstage1_temp.rows_amp = cfgstage1_temp.rows_fft;

    if(cfgstage1_temp.type_input == 1){
        cfgstage1_temp.cols_amp = cfgstage1_temp.cols_input;
        cfgstage1_temp.rows_amp = cfgstage1_temp.rows_input;
    }

    cfgstage1_temp.cols_trans = cfgstage1_temp.rows_amp;
    cfgstage1_temp.rows_trans = cfgstage1_temp.cols_amp;

    {  // ROI 范围适应
        if(cfgstage1_temp.roi_left >= cfgstage1_temp.cols_trans){
            cfgstage1_temp.roi_left = cfgstage1_temp.cols_trans - 1;
        }
        if(cfgstage1_temp.roi_top >= cfgstage1_temp.rows_trans){
            cfgstage1_temp.roi_top = cfgstage1_temp.rows_trans - 1;
        }

        cfgstage1_temp.cols_output = cfgstage1_temp.cols_trans - cfgstage1_temp.roi_left;
        cfgstage1_temp.rows_output = cfgstage1_temp.rows_trans - cfgstage1_temp.roi_top;
        if(cfgstage1_temp.cols_output < 1) cfgstage1_temp.cols_output = 1;
        if(cfgstage1_temp.rows_output < 1) cfgstage1_temp.rows_output = 1;

        cfgstage1_temp.cols_output = cfgstage1_temp.cols_output - cfgstage1_temp.roi_right;
        cfgstage1_temp.rows_output = cfgstage1_temp.rows_output - cfgstage1_temp.roi_bottom;
        if(cfgstage1_temp.cols_output < 1) cfgstage1_temp.cols_output = 1;
        if(cfgstage1_temp.rows_output < 1) cfgstage1_temp.rows_output = 1;

    }
    cfgstage1 = cfgstage1_temp;

    return true;
}

void kpextGetStage1OutputSize(int *cols, int *rows)
{
    if(cols){
        (*cols) = cfgstage1_temp.cols_output;
    }
    if(rows){
        (*rows) = cfgstage1_temp.rows_output;
    }
}

void kpextCfgStage1RingBufferCnt(int framecnt){
    if(stage1Working.load()){
        SPDLOG_WARN("Image Process stage1 is runing!");
        return;
    }

    if(framecnt > 0){
        stage1RingBufferFrames = framecnt;
    }else{
        stage1RingBufferFrames = 0;
    }
}

void kpextCfgStage2CMapLUT(unsigned int index,
                           const float *mapdata,
                           int len)
{
    if(len < 2){
        SPDLOG_ERROR("Invalid color map lut data!");
         return;
    }

    //    if(stage2Working.load()){
    //        SPDLOG_WARN("Set color map when Image Process Stage2 working!");
    //    }

    const std::vector<float> lut(mapdata, mapdata + 5 * len);
    OpImgCMapF32C1U8C4::setColorMap(index, lut);

    cfgstage2.colormapupdate = cfgstage2.colormapupdate + 1;
}

/*********************************************************************/

void kpextStartStage1()
{
    std::lock_guard<std::mutex> lk(stage1CtrlMutex);

    if(stage1Working.load()){
        SPDLOG_INFO("Image Process stage1 is runing!");
        return;
    }

    stage1Tasks.clear();
    stage1Threads.clear();
    stage1FrameBuf.clear();

    stage1RingBufferFrameIdx = 0;
    if(stage1RingBufferFrames > 0){
        size_t size = static_cast<size_t>(cfgstage1.cols_input * cfgstage1.rows_input);
        if(cfgstage1.type_input == 1) size = size * 2;
        for (int i = 0; i < stage1RingBufferFrames; ++i) {
            auto ptr = std::shared_ptr<uint16_t>(new uint16_t[size]);
            std::memset(ptr.get(), 0, size * 2);
            stage1FrameBuf.push_back(ptr);
        }
    }

    if(cfgstage1.avg_count < 1) cfgstage1.avg_count = 1;
    stage1Sem = nullptr;
    stage1Sem = std::make_shared<Stage1Semaphore>(cfgstage1.avg_count);
    stage1Sem.get()->setIsWakeupToQuit(false);
    for (uint32_t i = 0; i < stage1ThreadsCnt; ++i) {
        auto task = std::make_shared<Stage1Task>(stage1Work);
        stage1Tasks.push_back(task);
        auto thread = std::make_shared<std::thread>(
                    std::move(*stage1Tasks.back().get()),
                    stage1Op[i]
                    );
        stage1Threads.push_back(thread);
        stage1Sem->registerThreadID(
                    stage1Threads.back().get()->get_id()
                    );
    }

    stage1Sem->setAllThreadsRegistered();

    stage1Working.store(true);

    //    for (auto & t : stage1Threads) {
    //        t->detach();
    //    }
}

void kpextStopStage1()
{
    std::lock_guard<std::mutex> lk(stage1CtrlMutex);

    if(!stage1Working.load()){
        SPDLOG_INFO("Image Process stage1 is not runing!");
        return;
    }

    stage1Sem.get()->setIsWakeupToQuit(true);
    stage1Sem->wakeupAll();

    for (auto & t : stage1Threads) {
        if(t->joinable()) t->join();
    }

    stage1Working.store(false);

    stage1Tasks.clear();
    stage1Threads.clear();
    stage1RingBufferFrames = 0;
    stage1FrameBuf.clear();
    // stage1Sem = nullptr;
}


void kpextEnqueStage1(void *inptr, uint16_t *outptr)
{
    if(!stage1Working.load()){
        SPDLOG_INFO("Image Process stage1 is not runing!");
        return;
    }

    std::shared_ptr<Stage1Semaphore> t(nullptr);
    {
        std::lock_guard<std::mutex> lk(stage1CtrlMutex);
        t = stage1Sem;
    }

    if(t){
        int framebufcnt = static_cast<int>(stage1FrameBuf.size());
        if(framebufcnt > 0 && inptr){
            size_t inputsize = static_cast<size_t>(
                        cfgstage1.cols_input * cfgstage1.rows_input
                        );
            inputsize = inputsize * 2;
            if(cfgstage1.type_input == 1) inputsize = inputsize * 2;
            stage1RingBufferFrameIdx = stage1RingBufferFrameIdx % framebufcnt;
            auto rbuf = stage1FrameBuf[static_cast<size_t>(stage1RingBufferFrameIdx)];
            std::memcpy(rbuf.get(), inptr, inputsize);
            Stage1InputSZ in{rbuf.get(), rbuf.get()};
            t->pushInput(in);
            stage1RingBufferFrameIdx++;
        }else{
            Stage1InputSZ in{inptr, outptr};
            t->pushInput(in);
        }
    }
}

unsigned short * kpextWaitStage1Output(int timeout_ms, unsigned short * clonedst)
{
    unsigned short * ret = nullptr;

    if(!stage1Working.load()) return ret;

    std::shared_ptr<Stage1Semaphore> t(nullptr);
    {
        std::lock_guard<std::mutex> lk(stage1CtrlMutex);
        t = stage1Sem;
    }

    if(t){
        t->acquireOutput(& ret, timeout_ms);
        if(ret && clonedst){
            size_t size = static_cast<size_t>(
                        cfgstage1.cols_output * cfgstage1.rows_output
                        );
            size = size * 2;
            std::memcpy(clonedst, ret, size);
        }
    }
    return ret;
}

void kpextStartStage2()
{
    std::lock_guard<std::mutex> lk(stage2CtrlMutex);

    if(stage2Working.load()){
        SPDLOG_INFO("Image Process stage2 is runing!");
        return;
    }

    stage2Tasks.clear();
    stage2Threads.clear();

    stage2Sem = nullptr;
    stage2Sem = std::make_shared<Stage2Semaphore>(1);
    stage2Sem.get()->setIsWakeupToQuit(false);
    for (uint32_t i = 0; i < stage2ThreadsCnt; ++i) {
        auto task = std::make_shared<Stage2Task>(stage2Work);
        stage2Tasks.push_back(task);

        auto thread = std::make_shared<std::thread>(
                    std::move(*stage2Tasks.back().get()),
                    stage2Op[i]
                    );
        stage2Threads.push_back(thread);
        stage2Sem->registerThreadID(
                    stage2Threads.back().get()->get_id()
                    );
    }
    stage2Sem->setAllThreadsRegistered();

    stage2Working.store(true);
}

void kpextStopStage2(){
    std::lock_guard<std::mutex> lk(stage2CtrlMutex);

    if(!stage2Working.load()){
        SPDLOG_INFO("Image Process stage2 is not runing!");
        return;
    }

    stage2Sem.get()->setIsWakeupToQuit(true);
    stage2Sem->wakeupAll();

    for (auto & t : stage2Threads) {
        if(t->joinable()) t->join();
    }

    stage2Working.store(false);
    stage2Tasks.clear();
    stage2Threads.clear();
    // stage2Sem = nullptr;
}


void kpextEnqueStage2(int cols, int rows,
                      unsigned short * inptr,
                      unsigned char * outptr){
    if(!stage2Working.load()){
        SPDLOG_INFO("Image Process stage2 is not runing!");
        return;
    }

    std::shared_ptr<Stage2Semaphore> t(nullptr);
    {
        std::lock_guard<std::mutex> lk(stage2CtrlMutex);
        t = stage2Sem;
    }

    if(t){
        Stage2InputSZ in{cols, rows, inptr, outptr};
        t->pushInput(in);
    }
}


unsigned char * kpextWaitStage2Output(int * cols, int *rows,
                                      int timeout_ms){
    Stage2OutputSZ ret{0, 0, nullptr};

    if(!stage2Working.load()) return ret.dptr;

    std::shared_ptr<Stage2Semaphore> t(nullptr);
    {
        std::lock_guard<std::mutex> lk(stage2CtrlMutex);
        t = stage2Sem;
    }

    if(t){
        t->acquireOutput(& ret, timeout_ms);
    }else{
        return nullptr;
    }

    if(cols){
        (*cols) = ret.cols;
    }

    if(rows){
        (*rows) = ret.rows;
    }

    return ret.dptr;
}

void kpextSetStage2HFlip(bool enable){
    cfgstage2.enable_hflip = enable;
}

void kpextSetStage2VFlip(bool enable){
    cfgstage2.enable_vflip = enable;
}

void kpextSetStage2Beta(float b){
    cfgstage2.offset_b = b;
}

void kpextSetStage2Mul(float a){
    cfgstage2.mul_a = a;
}

void kpextSetStage2Threshold(unsigned short thres){
    cfgstage2.threshold = thres;
}

void kpextSetStage2Power(float y){
    cfgstage2.power_y = y;
}

void kpextSetStage2CMap(unsigned int idx){
    cfgstage2.colormap = idx;
}

/************************************** 各图像处理阶段的工作内容 ***************************/
#define NS_5E8 500000000U   // 500毫秒的 纳秒表示
void stage1Work(Stage1OpSZ & op){
    if(!stage1Sem->waitAllThreadsRegistered(2000)){
        throw std::runtime_error(__FUNCTION__ " wait Threads register timeout !");
    }

    auto subrec = op.cmdbuf->allocateCmdBuf(true, vk::CommandPoolCreateFlags());

    {   //  初始化vulkan compute 资源
        CmdBaseBufferCpy bufcpy;
        CmdBaseBufferSet bufset;

        if(!op.inbarrier){
            op.inbarrier = std::make_shared<CmdBuffersMemBarrier>();
        }
        if(!op.outbarrier){
            op.outbarrier = std::make_shared<CmdBuffersMemBarrier>();
        }

        //  申请vulkan设备缓冲
        size_t size = static_cast<size_t>(cfgstage1.cols_fft * cfgstage1.rows_fft);
        size = size * sizeof (float) * 2;

        op.inputMem = GvkContextPtr->stagingBuf(size);
        //  在笔记本核显上使用 visible + cache 会出现显存无法及时同步
        //到主机内存的问题，只使用coherent出现的问题少一点，但也无法完全消失
        //即使显式调用invalidateMapped也没有用
        //  目前不清楚是不是vulkan核显驱动问题还是其它，暂还是使用 visible+cache的组合
        op.outputMem = GvkContextPtr->stagingBuf(size, false, true);

        if(fabs(cfgstage1.resample_delta) > 1e-10f){
            auto bytesize = static_cast<size_t>(cfgstage1.cols_input) * sizeof (float);
            op.devIdxBuf = GvkContextPtr->deviceBuf(bytesize);

            std::shared_ptr<float> idxdata(genDCResampleIdx(cfgstage1.cols_input, cfgstage1.resample_delta));
            auto stageMem = GvkContextPtr->stagingBuf(bytesize);
            auto stageptr = stageMem->data<float>();
            std::memcpy(stageptr, idxdata.get(), bytesize);
            stageMem->unmap();
            auto trec = op.cmdbuf->allocateCmdBuf(true, vk::CommandPoolCreateFlagBits::eTransient);
            trec->begin();
            bufcpy.prepare(op.devIdxBuf, stageMem);
            trec->record(&bufcpy);
            trec->end();
            trec->submit();
            trec->waitExec(NS_5E8);
        }

        op.devBuf1 = GvkContextPtr->deviceBuf(size);
        op.devBuf2 = GvkContextPtr->deviceBuf(size);
        op.devBuf3 = GvkContextPtr->deviceBuf(size);

        op.devAvgBuf = GvkContextPtr->deviceBuf(size);

        //  申请图像操作对象
        if(!op.ccvtU16C1toF32C1){
            op.ccvtU16C1toF32C1 = std::shared_ptr<OpImgCvtU16C1toF32C1>(
                        new OpImgCvtU16C1toF32C1(GvkContextPtr)
                        );
        }
        if(!op.cresampleU16C1toF32C1){
            op.cresampleU16C1toF32C1 = std::shared_ptr<OpImgResampleU16C1toF32C1>(
                        new OpImgResampleU16C1toF32C1(GvkContextPtr)
                        );
        }

        if(!op.ccpyF32C1){
            op.ccpyF32C1 = std::shared_ptr<OpImgCpyF32C1>(
                        new OpImgCpyF32C1(GvkContextPtr)
                        );
        }

        if(!op.crwinF32C1){
            op.crwinF32C1 = std::shared_ptr<OpImgRWinF32C1>(
                        new OpImgRWinF32C1(GvkContextPtr)
                        );
        }

        if(!op.ccvtF32C1toF32C2){
            op.ccvtF32C1toF32C2 = std::shared_ptr<OpImgCvtF32C1toF32C2>(
                        new OpImgCvtF32C1toF32C2(GvkContextPtr)
                        );
        }

        if(!op.cF32C2fft){
            op.cF32C2fft = std::shared_ptr<OpImgF32C2FFT>(
                        new OpImgF32C2FFT(GvkContextPtr)
                        );
        }

        if(!op.cF32C2amp){
            op.cF32C2amp = std::shared_ptr<OpImgF32C2Magnitude>(
                        new OpImgF32C2Magnitude(GvkContextPtr)
                        );
        }

        if(!op.cF32C1Avg){
            op.cF32C1Avg = std::shared_ptr<OpImgF32C1Average>(
                        new OpImgF32C1Average(GvkContextPtr)
                        );
        }

        if(!op.cF32C1Trans){
            op.cF32C1Trans = std::shared_ptr<OpImgF32C1Transpose>(
                        new OpImgF32C1Transpose(GvkContextPtr)
                        );
        }

        if(!op.cF32C1ROI){
            op.cF32C1ROI = std::shared_ptr<OpImgF32C1ROI>(
                        new OpImgF32C1ROI(GvkContextPtr)
                        );
        }

        if(!op.cF32C1Flip){
            op.cF32C1Flip = std::shared_ptr<OpImgF32C1Flip>(
                        new OpImgF32C1Flip(GvkContextPtr)
                        );
        }

        if(!op.ccvtF32C1toU16C1){
            op.ccvtF32C1toU16C1 = std::shared_ptr<OpImgCvtF32C1toU16C1>(
                        new OpImgCvtF32C1toU16C1(GvkContextPtr)
                        );
        }

        //  图像操作重建
        if(fabs(cfgstage1.resample_delta) > 1e-10f){
            op.cresampleU16C1toF32C1->rebuild(
                        cfgstage1.cols_input,
                        cfgstage1.rows_input,
                        op.devBuf1,
                        op.devIdxBuf,
                        op.devBuf2
                        );
        }else{
            op.ccvtU16C1toF32C1->rebuild(
                        cfgstage1.cols_input,
                        cfgstage1.rows_input,
                        op.devBuf1,
                        op.devBuf2
                        );
        }

        op.ccpyF32C1->rebuild(
                    cfgstage1.cols_input,
                    cfgstage1.rows_input,
                    cfgstage1.cols_fft,
                    cfgstage1.rows_fft,
                    op.devBuf2,
                    op.devBuf1
                    );

        op.crwinF32C1->rebuild(
                    cfgstage1.cols_fft,
                    cfgstage1.rows_fft,
                    op.devBuf1,
                    op.devBuf2,
                    op.cmdbuf,
                    static_cast<uint32_t>(cfgstage1.win_type),
                    static_cast<uint32_t>(cfgstage1.cols_input)
                    );

        op.ccvtF32C1toF32C2->rebuild(
                    cfgstage1.cols_fft,
                    cfgstage1.rows_fft,
                    op.devBuf2,
                    op.devBuf3
                    );

        op.cF32C2fft->rebuild(
                    cfgstage1.cols_fft,
                    cfgstage1.rows_fft,
                    op.devBuf3,
                    op.devBuf2,
                    op.cmdbuf,
                    0,
                    1 / cfgstage1_temp.prescale
                    );
        auto tbuf = std::dynamic_pointer_cast<kpext::DeviceBuffer>(
                    op.cF32C2fft->whichOutput(op.devBuf2, op.devBuf3)
                    );

        op.cF32C2amp->rebuild(
                    cfgstage1.cols_fft,
                    cfgstage1.rows_fft,
                    tbuf,
                    op.devBuf1,
                    cfgstage1.cols_amp,
                    !cfgstage1.disable_log,
                    1,
                    cfgstage1.alpha,
                    cfgstage1.beta
                    );

        op.cF32C1Avg->rebuild(
                    cfgstage1.cols_amp,
                    cfgstage1.rows_amp,
                    1 / static_cast<float>(cfgstage1.avg_count),
                    op.devBuf1,
                    op.devAvgBuf
                    );

        if(cfgstage1.avg_count > 1){
            tbuf = op.devAvgBuf;
        }else{
            tbuf = op.devBuf1;
        }

        op.cF32C1Trans->rebuild(
                    cfgstage1.cols_amp,
                    cfgstage1.rows_amp,
                    tbuf,
                    op.devBuf2
                    );

        op.cF32C1ROI->rebuild(
                    cfgstage1.cols_trans,
                    cfgstage1.rows_trans,
                    cfgstage1.cols_output,
                    cfgstage1.rows_output,
                    cfgstage1.roi_left,
                    cfgstage1.roi_top,
                    op.devBuf2,
                    op.devBuf1
                    );

        op.cF32C1Flip->rebuild(
                    cfgstage1.cols_output,
                    cfgstage1.rows_output,
                    op.devBuf1,
                    op.devBuf2,
                    cfgstage1.enable_hflip,
                    cfgstage1.enable_vflip
                    );

        if(!cfgstage1.enable_hflip && !cfgstage1.enable_vflip){
            tbuf = op.devBuf1;
        }else{
            tbuf = op.devBuf2;
        }

        op.ccvtF32C1toU16C1->rebuild(
                    cfgstage1.cols_output,
                    cfgstage1.rows_output,
                    tbuf,
                    op.devBuf3
                    );
        op.ccvtF32C1toU16C1->enablePostBarrier(false);

        //  开始记录
        size = static_cast<size_t>(cfgstage1.cols_input * cfgstage1.rows_input) * 2;

        subrec->begin();
        bufcpy.prepare(op.devBuf1, op.inputMem, size);
        subrec->record(&bufcpy);
        op.inbarrier->prepare(op.devBuf1,
                              vk::PipelineStageFlagBits::eTransfer,
                              vk::PipelineStageFlagBits::eComputeShader,
                              vk::AccessFlagBits::eTransferWrite,
                              vk::AccessFlagBits::eShaderRead
                              );
        subrec->record(op.inbarrier.get());

        if(cfgstage1.type_input == 0){
            if(fabs(cfgstage1.resample_delta) > 1e-10f){
                subrec->record(op.cresampleU16C1toF32C1.get());
            }else{
                subrec->record(op.ccvtU16C1toF32C1.get());
            }
            subrec->record(op.ccpyF32C1.get());
            subrec->record(op.crwinF32C1.get());
            subrec->record(op.ccvtF32C1toF32C2.get());
            subrec->record(op.cF32C2fft.get());
            subrec->record(op.cF32C2amp.get());
        }
        if(cfgstage1.avg_count > 1){
            subrec->record(op.cF32C1Avg.get());
        }
        subrec->record(op.cF32C1Trans.get());
        subrec->record(op.cF32C1ROI.get());
        if(cfgstage1.enable_hflip || cfgstage1.enable_vflip){
            subrec->record(op.cF32C1Flip.get());
        }
        subrec->record(op.ccvtF32C1toU16C1.get());

        op.outbarrier->prepare(op.devBuf3,
                               vk::PipelineStageFlagBits::eComputeShader,
                               vk::PipelineStageFlagBits::eTransfer,
                               vk::AccessFlagBits::eShaderWrite,
                               vk::AccessFlagBits::eTransferRead
                               );
        subrec->record(op.outbarrier.get());

        size = static_cast<size_t>(cfgstage1.cols_output * cfgstage1.rows_output) * 2;
        bufcpy.prepare(op.outputMem, op.devBuf3, size);
        subrec->record(&bufcpy);
        subrec->end();

    }

    //  图像处理循环
    int avgidx = 0;
    do{
        Stage1InputSZ dataPtr{};
        if(! stage1Sem->acquireInput(&dataPtr, 50)){
            if(stage1Sem->getIsWakeupToQuit()){
                break;
            }
            continue;
        }
        if(!dataPtr.inptr) continue;

//        {
//            std::hash<std::thread::id> hasher;
//            SPDLOG_INFO(__FUNCTION__ "({:X}) acquired .",
//                        hasher(std::this_thread::get_id())
//                        );
//        }

        size_t size;
        if(cfgstage1.avg_count > 1 && avgidx == 0){
            //  需要平均时，第一次输入平均缓冲清零
            CmdBaseBufferSet bufset;
            CmdBuffersMemBarrier barrier;
            auto trec = op.cmdbuf->allocateCmdBuf(true, vk::CommandPoolCreateFlagBits::eTransient);
            size = static_cast<size_t>(cfgstage1.cols_amp * cfgstage1.rows_amp) * 4;
            bufset.prepare(op.devAvgBuf, 0, size);
            barrier.prepare(op.devAvgBuf,
                            vk::PipelineStageFlagBits::eTransfer,
                            vk::PipelineStageFlagBits::eComputeShader,
                            vk::AccessFlagBits::eTransferWrite,
                            vk::AccessFlagBits::eShaderRead);
            trec->begin();
            trec->record(&bufset);
            trec->record(&barrier);
            trec->end();
            trec->submit();

            //  复制输入
            size = static_cast<size_t>(cfgstage1.cols_input * cfgstage1.rows_input) * 2;
            if(cfgstage1.type_input == 1) size = size * 2;  //  32位浮点数据
            std::memcpy(op.inputMem->map(),
                        dataPtr.inptr,
                        size
                        );
            op.inputMem->unmap();

            trec->waitExec(NS_5E8);
        }else{
            //  复制输入
            size = static_cast<size_t>(cfgstage1.cols_input * cfgstage1.rows_input) * 2;
            if(cfgstage1.type_input == 1) size = size * 2;  //  32位浮点数据
            std::memcpy(op.inputMem->map(),
                        dataPtr.inptr,
                        size
                        );
            op.inputMem->unmap();
        }

        subrec->submit();
        subrec->waitExec(NS_5E8);

        if(dataPtr.outptr && (avgidx == cfgstage1.avg_count - 1)){
            //  复制输出
            size = static_cast<size_t>(cfgstage1.cols_output * cfgstage1.rows_output) * 2;
            auto src = op.outputMem->map();
            op.outputMem->invalidateMapped();
            std::memcpy(dataPtr.outptr, src, size);
            op.outputMem->unmap();

            stage1Sem->pushOutput(dataPtr.outptr);
        }

        avgidx++;
        avgidx = avgidx % cfgstage1.avg_count;
    }while(1);

    subrec->waitExec(NS_5E8);
    subrec = nullptr;
}

void stage2Work(Stage2OpSZ & op){
    if(!stage2Sem->waitAllThreadsRegistered(2000)){
        throw std::runtime_error(__FUNCTION__ " wait Threads register timeout !");
    }

    bool needresize = false;
    bool needrebuild = true;
    bool needrefreshcm = false;
    int elemsize = 0;
    size_t size;

    CfgStage2 cfg = cfgstage2;

    { // 图像处理阶段2 的操作杂项资源初始化
        if(!op.framesize){
            op.framesize = std::make_shared<Stage2OpSZ::Size2D>();
            op.framesize->cols = 512;
            op.framesize->rows = 512;
            needresize = true;
        }

        if(!op.inbarrier){
            op.inbarrier = std::make_shared<CmdBuffersMemBarrier>();
        }

        if(!op.outbarrier){
            op.outbarrier = std::make_shared<CmdBuffersMemBarrier>();
        }

        if(!op.ccvtU16C1toF32C1){
            op.ccvtU16C1toF32C1 = std::make_shared<OpImgCvtU16C1toF32C1>(GvkContextPtr);
        }

        if(!op.cF32C1Flip){
            op.cF32C1Flip = std::make_shared<OpImgF32C1Flip>(GvkContextPtr);
        }

        if(!op.cF32C1MulPow){
            op.cF32C1MulPow = std::make_shared<OpImgF32C1MulPow>(GvkContextPtr);
        }

        if(!op.ccmapF32C1toU8C4){
            op.ccmapF32C1toU8C4 = std::make_shared<OpImgCMapF32C1U8C4>(GvkContextPtr);
            op.ccmapF32C1toU8C4->enablePostBarrier(false);
        }

        if(op.inputMem){
            elemsize = static_cast<int>(op.inputMem->getSize() / 4);
            elemsize = elemsize - 4;
            if(elemsize < 0) elemsize = 0;
        }
    }

    do{
        Stage2InputSZ dataPtr{};
        if(! stage2Sem->acquireInput(&dataPtr, 50)){
            if(stage2Sem->getIsWakeupToQuit()){
                break;
            }
            continue;
        }

        //        {
        //            std::hash<std::thread::id> hasher;
        //            SPDLOG_INFO(__FUNCTION__ "({:X}) acquired .",
        //                        hasher(std::this_thread::get_id())
        //                        );
        //        }

        if(dataPtr.cols > 0
                && dataPtr.rows > 0
                && dataPtr.inptr
                ){
            if(elemsize < dataPtr.cols * dataPtr.rows){
                needresize = true;
            }
            if(op.framesize->cols != dataPtr.cols
                    || op.framesize->rows != dataPtr.rows
                    ){
                needrebuild = true;
            }
            op.framesize->cols = dataPtr.cols;
            op.framesize->rows = dataPtr.rows;
        }

        if(elemsize < 1){
            needresize = true;
        }


        if(needresize){  //  重申请vulkan设备显存
            op.cmdbuf->reset();

            elemsize = op.framesize->cols * op.framesize->rows;
            size = static_cast<size_t>(elemsize + 4);
            size = size * 4;
            op.inputMem = GvkContextPtr->stagingBuf(size);
            op.outputMem = GvkContextPtr->stagingBuf(size, false, true);

            op.devBuf1 = GvkContextPtr->deviceBuf(size);
            op.devBuf2 = GvkContextPtr->deviceBuf(size);
            op.devBuf3 = GvkContextPtr->deviceBuf(size);

            op.inbarrier->prepare(
                        op.devBuf1,
                        vk::PipelineStageFlagBits::eTransfer,
                        vk::PipelineStageFlagBits::eComputeShader,
                        vk::AccessFlagBits::eTransferWrite,
                        vk::AccessFlagBits::eShaderRead
                        );

            op.outbarrier->prepare(
                        op.devBuf3,
                        vk::PipelineStageFlagBits::eComputeShader,
                        vk::PipelineStageFlagBits::eTransfer,
                        vk::AccessFlagBits::eShaderWrite,
                        vk::AccessFlagBits::eTransferRead
                        );

            needresize = false;
            needrebuild = true;
        }

        { // 常量修改判断
            needrebuild = needrebuild || (cfg.enable_hflip != cfgstage2.enable_hflip);
            needrebuild = needrebuild || (cfg.enable_vflip != cfgstage2.enable_vflip);
            needrebuild = needrebuild || (cfg.threshold != cfgstage2.threshold);
            needrebuild = needrebuild || (cfg.offset_b != cfgstage2.offset_b);
            needrebuild = needrebuild || (cfg.mul_a != cfgstage2.mul_a);
            needrebuild = needrebuild || (cfg.power_y != cfgstage2.power_y);
            needrebuild = needrebuild || (cfg.colormap != cfgstage2.colormap);
            needrefreshcm = cfg.colormapupdate != cfgstage2.colormapupdate;
            cfg = cfgstage2;
        }

        if(needrebuild){  //  重建图像处理操作
            op.cmdbuf->reset();

            op.ccvtU16C1toF32C1->rebuild(
                        op.framesize->cols,
                        op.framesize->rows,
                        op.devBuf1,
                        op.devBuf2
                        );

            op.cF32C1Flip->rebuild(
                        op.framesize->cols,
                        op.framesize->rows,
                        op.devBuf2,
                        op.devBuf1,
                        cfg.enable_hflip,
                        cfg.enable_vflip
                        );

            std::shared_ptr<DeviceBuffer> tbuf = op.devBuf1;
            if(cfg.enable_hflip || cfg.enable_vflip){
                op.cF32C1MulPow->rebuild(
                            op.framesize->cols,
                            op.framesize->rows,
                            cfg.threshold,
                            cfg.offset_b,
                            cfg.mul_a,
                            cfg.power_y,
                            op.devBuf1,
                            op.devBuf2
                            );
                tbuf = op.devBuf2;
            }else{
                op.cF32C1MulPow->rebuild(
                            op.framesize->cols,
                            op.framesize->rows,
                            cfg.threshold,
                            cfg.offset_b,
                            cfg.mul_a,
                            cfg.power_y,
                            op.devBuf2,
                            op.devBuf1
                            );
            }

            op.ccmapF32C1toU8C4->rebuild(
                        op.framesize->cols,
                        op.framesize->rows,
                        cfg.colormap,
                        tbuf,
                        op.devBuf3,
                        op.cmdbuf
                        );

            kpext::CmdBaseBufferCpy cmdcpy;

            op.cmdbuf->begin();

            size = static_cast<size_t>(op.framesize->cols * op.framesize->rows) * 2;
            cmdcpy.prepare(
                        op.devBuf1,
                        op.inputMem,
                        size
                        );
            op.cmdbuf->record(&cmdcpy);
            op.cmdbuf->record(op.inbarrier.get());

            op.cmdbuf->record(op.ccvtU16C1toF32C1.get());
            if(cfg.enable_hflip || cfg.enable_vflip){
                op.cmdbuf->record(op.cF32C1Flip.get());
            }
            op.cmdbuf->record(op.cF32C1MulPow.get());
            op.cmdbuf->record(op.ccmapF32C1toU8C4.get());

            op.cmdbuf->record(op.outbarrier.get());
            size = size * 2;
            cmdcpy.prepare(
                        op.outputMem,
                        op.devBuf3,
                        size
                        );
            op.cmdbuf->record(&cmdcpy);

            op.cmdbuf->end();

            needrebuild = false;
        }

        if(needrefreshcm){
            op.ccmapF32C1toU8C4->refreshColorMap(op.cmdbuf);
            needrefreshcm = false;
        }

        if(!dataPtr.outptr && !dataPtr.inptr){
            auto size = op.inputMem->getSize();
            auto ptr = op.inputMem->map();
            std::memset(ptr, 0, size);
            op.inputMem->unmap();
        }

        if(dataPtr.inptr){  //  复制输入数据
            size = static_cast<size_t>(op.framesize->cols * op.framesize->rows);
            size = size * 2;
            auto dst = op.inputMem->map();
            std::memcpy(dst, dataPtr.inptr, size);
            op.inputMem->unmap();
        }

        op.cmdbuf->submit();
        op.cmdbuf->waitExec(NS_5E8);


        if(!dataPtr.outptr){
            continue;
        }

        {  //  复制输出数据
            size = static_cast<size_t>(op.framesize->cols * op.framesize->rows);
            size = size * 4;
            auto src = op.outputMem->map();
            op.outputMem->invalidateMapped();
            std::memcpy(dataPtr.outptr, src, size);
            op.outputMem->unmap();

            Stage2OutputSZ out{op.framesize->cols,
                        op.framesize->rows,
                        dataPtr.outptr
                              };

            stage2Sem->pushOutput(out);
        }

    }while(1);

    op.cmdbuf->waitExec(NS_5E8);
    op.cmdbuf->reset();
}

/**************************************************************************************/
